use libp2p::swarm::SwarmEvent;

use tracing::{error, info, warn};

use crate::network_api::P2PEventForApp;
use crate::network_core::P2pNetWorkError;
use crate::network_core::network_core_impl::combined_behaviour::CombinedBehaviourEvent;
use crate::network_core::network_core_impl::network_manager::NetworkManager;

impl NetworkManager {
    // network to local event loop
    pub(super) async fn handle_and_dispatch_event(
        &mut self,
        event: SwarmEvent<CombinedBehaviourEvent>,
    ) {
        info!(">>>>>>>>> Handling network event {:?}.", event);
        match event {
            SwarmEvent::NewListenAddr { address, .. } => {
                let local_peer_id = *self.swarm.local_peer_id();
                println!("[{}] Listening on {}", local_peer_id, address);
                // eprintln!(
                //     "Local node is listening on {:?}",
                //     address.with(Protocol::P2p(local_peer_id))
                // );
                match self
                    .event_sender_for_app
                    .send(P2PEventForApp::NewListenAddr(address))
                    .await
                {
                    Ok(_) => {}
                    Err(e) => {
                        panic!("Failed to send event to app: {}", e);
                    }
                }
            }

            SwarmEvent::NewExternalAddrCandidate { address } => {
                // We have discovered a new candidate for an external address for us.
                println!("New external address candidate: {:?}", address);
            }

            SwarmEvent::IncomingConnection { .. } => {
                info!("Incoming connection")
            }
            SwarmEvent::ConnectionEstablished {
                peer_id, endpoint, ..
            } => {
                info!("Connection established with {peer_id}");
                if endpoint.is_dialer() {
                    if let Some(sender) = self.pending_dial.remove(&peer_id) {
                        let _ = sender.send(Ok(()));
                    }
                }
            }
            SwarmEvent::ConnectionClosed { .. } => {
                info!("Connection closed")
            }
            SwarmEvent::OutgoingConnectionError {
                connection_id,
                peer_id,
                error,
            } => {
                error!("Outgoing connection error: {error}. {peer_id:?}, {connection_id:?}");
                if let Some(peer_id) = peer_id {
                    if let Some(sender) = self.pending_dial.remove(&peer_id) {
                        let _ = sender.send(Err(P2pNetWorkError::SwarmDialError(error)));
                    }
                }
            }
            SwarmEvent::IncomingConnectionError {
                connection_id,
                local_addr,
                send_back_addr,
                error,
            } => {
                warn!(
                    "Incoming connection error: {error:?}. Local address: {local_addr}, send back address: {send_back_addr} connection id: {connection_id}\n"
                )
            }
            SwarmEvent::Dialing {
                peer_id: Some(peer_id),
                ..
            } => eprintln!("Dialing {peer_id}"),

            SwarmEvent::NewExternalAddrOfPeer { peer_id, address } => {
                info!("New external address of {peer_id}: {address}")
            }

            SwarmEvent::Behaviour(CombinedBehaviourEvent::Kademlia(kad_event)) => {
                self.swarm
                    .behaviour_mut()
                    .handle_kademlia_event(&mut self.kad_metadata, kad_event);
            }

            SwarmEvent::Behaviour(CombinedBehaviourEvent::RequestResponse(
                request_response_event,
            )) => {
                self.swarm
                    .behaviour_mut()
                    .handle_request_response_event(
                        &mut self.request_response_metadata,
                        request_response_event,
                    )
                    .await;
            }

            SwarmEvent::Behaviour(CombinedBehaviourEvent::Mdns(mdns_event)) => {
                // let local_peer_id = *self.swarm.local_peer_id();
                self.swarm
                    .behaviour_mut()
                    .handle_mdns_event(mdns_event, &self.local_peer_id);
            }

            SwarmEvent::Behaviour(CombinedBehaviourEvent::Gossipsub(gossipsub_event)) => {
                self.swarm
                    .behaviour_mut()
                    .handle_gossipsub_event(
                        gossipsub_event,
                        &self.local_peer_id,
                        &self.event_sender_for_app,
                    )
                    .await;
            }

            SwarmEvent::Behaviour(CombinedBehaviourEvent::Identify(identify_event)) => {
                self.swarm
                    .behaviour_mut()
                    .handle_identify_event(identify_event);
            }

            event => panic!("{event:#?}"),
        }
    }
}
